五.任务状态段和控制门
每个任务有一个任务状态段TSS,用于保存任务的有关信息,在任务内变换特权级和任务切换时,
要用到这些信息。为了控制任务内发生特权级变换的转移,为了控制任务切换,一般要通过控制
门进行这些转移。本文将介绍任务状态段和控制门。
<一>系统段描述符
系统段是为了实现存储管理机制所使用的一种特别的段。在80386中,有两种系统段:任务状态
段TSS和局部描述符表LDT段。用于描述系统段的描述符称为系统段描述符。
1.系统段描述符的格式
系统段描述符的一般格式如下表所示。
系统段 描述符 | 
m+7 | 
m+6 | 
m+5 | 
m+4 | 
m+3 | 
m+2 | 
m+1 | 
m+0 | 
| Base(31...24) | 
Attributes | 
Segment Base(23...0) | 
Segment Limite(15...0) | 
系统段 描述符 的属性 | 
Byte m+6 | 
Byte m+5 | 
| BIT7 | 
BIT6 | 
BIT5 | 
BIT4 | 
BIT3 | 
BIT2 | 
BIT1 | 
BIT0 | 
BIT7 | 
BIT6 | 
BIT5 | 
BIT4 | 
BIT3 | 
BIT2 | 
BIT1 | 
BIT0 | 
| G | 
X | 
0 | 
AVL | 
Limit(19...16) | 
P | 
DPL | 
DT0 | 
TYPE | 
与存储段描述符相比,它们很相似,区分的标志是属性字节中的描述符类型位DT的值。DT=1表示
存储段,DT=0表示系统段。系统段描述符中的段基地址和段界限字段与存储段描述符中的意义完
全相同;属性中的G位、AVL位、P位和DPL字段的作用也完全相同。存储段描述符属性中的D位在
系统段描述符中不使用,现用符号X表示。系统段描述符的类型字段TYPE仍是4位,其编码及表示
的类型列于下表,其含义与存储段描述符的类型却完全不同。
    
    
    系统段 类  型 | 
    类型编码 | 
    说      明 | 
     
    
    | 0 | 
    未定义 | 
     
    
    | 1 | 
    可用286TSS | 
     
    
    | 2 | 
    LDT | 
     
    
    | 3 | 
    忙的286TSS | 
     
    
    | 4 | 
    286调用门 | 
     
    
    | 5 | 
    任务门 | 
     
    
    | 6 | 
    286中断门 | 
     
    
    | 7 | 
    286陷阱门 | 
     
     
 | 
 | 
    
    
    系统段 类  型 | 
    类型编码 | 
    说      明 | 
     
    
    | 8 | 
    未定义 | 
     
    
    | 9 | 
    可用386TSS | 
     
    
    | A | 
    未定义 | 
     
    
    | B | 
    忙的386TSS | 
     
    
    | C | 
    386调用门 | 
     
    
    | D | 
    未定义 | 
     
    
    | E | 
    386中断门 | 
     
    
    | F | 
    386陷阱门 | 
     
     
 | 
从上表可见,只有类型编码为2、1、3、9和B的描述符才是真正的系统段描述符,它们用于描述
系统段LDT和任务状态段TSS,其它类型的描述符是门描述符。
利用前文定义的存储段描述符结构类型DESC仍能方便地在程序中说明系统段描述符。需要注意
的是,系统段描述符的选择子不能用来读写系统段,要想读写系统段,必须使用别名技术。
2.LDT段描述符
LDT段描述符描述任务的局部描述符表段。例如:下面的描述符LDTABLE描述一个局部描述符表
段,基地址是654321H,以字节为单位的界限是1FH,描述符特权级是0。
    LDTABLE  DESC  <1FH,4321H,65H,82H,,>
LDT段描述符必须安排在全局描述符表中才有效。在装载LDTR寄存器时,描述符中的LDT段基地
址和段界限等信息被装入LDT段描述符高速缓冲寄存器中。
3.任务状态段描述符
任务状态段TSS用于保存任务的各种状态信息。任务状态段描述符描述某个任务状态段TSS描述符
分为286TSS和386TSS两类。TSS描述符规定了任务状态段的基地址和任务状态段的大小等信息。
例如,下面的描述符TempTask描述一个可用的386任务状态段,基地址是123456H,以字节为单位
的界限是104,描述符特权级是0。
    TempTask  DESC  <104,3456H,12H,89H,,>
在装载任务状态段寄存器TR时,描述符中的段基地址和段界限等信息被装入到TR的高速缓冲寄存
器中。在任务切换或执行LTR指令时,要装载TR寄存器。
TSS描述符中的类型规定:TSS要么为“忙”,要么为“可用”。如果一个任务是当前正执行的任
务,或者是用TSS中的链接字段沿挂起任务链接到当前任务上的任务,那么该任务是“忙”的任
务;否则该任务为“可用”任务。
利用段间转移指令JMP和段间调用指令CALL,直接通过TSS描述符或通过任务门可实现任务切换。
<二>门描述符
除存储段描述符和系统段描述符外,还有一类门描述符。门描述符并不描述某种内存段,而是描
述控制转移的入口点。这种描述符好比一个同向另一代码段的门。通过这种门,可实现任务内特
权级的变换和任务间的切换。所以,这种门描述符也称为控制门。
1.门描述符的一般格式
门描述符的一般格式如下图所示。门描述符只有位于描述符内偏移5的类型字节与系统段保持一
致,也由该字节标示门描述符和系统段描述符。该字节内的P和DPL的意义与其它描述符种中的
意义相同。其它字节主要用于存放一个48位的全指针(16位的选择子和32位的偏移量)。
| 门描述符 | 
m+7 | 
m+6 | 
m+5 | 
m+4 | 
m+3 | 
m+2 | 
m+1 | 
m+0 | 
| Offset(31...16) | 
Attributes | 
Selector | 
Offset(15...0) | 
门描述 符属性 | 
Byte m+5 | 
Byte m+4 | 
| BIT7 | 
BIT6 | 
BIT5 | 
BIT4 | 
BIT3 | 
BIT2 | 
BIT1 | 
BIT0 | 
BIT7 | 
BIT6 | 
BIT5 | 
BIT4 | 
BIT3 | 
BIT2 | 
BIT1 | 
BIT0 | 
| P | 
DPL | 
DT0 | 
TYPE | 
000 | 
Dword Count | 
根据上图给出的门描述符的结构,可定义如下的门描述符结构类型:
    GATE     STRUC      
    OFFSETL  DW      0  
    SELECTOR DW      0  
    DCOUNT   DB      0  
    GTYPE    DB      0  
    OFFSETH  DW      0  
    GATE     ENDS
利用门描述符结构类型GATE能方便地在程序中说明门描述符。
例如,下面的门描述符SUBRG描述一个386调用门,门内的选择子是10H,入口偏移是123456H,门
描述符特权级是3,双字计数是0。
    SUBRG    GATE  <3456,10H,,8CH+60H,12H>
从上述描述符类型的列表中可见,门描述符又可分为:任务门、调用门、中断门和陷阱门,并且
除任务门外,其它描述符还各分成286和386两种。
2.调用门
调用门描述某个子程序的入口。调用门内的选择子必须实现代码段描述符,调用门内的偏移是对
应代码段内的偏移。利用段间调用指令CALL,通过调用门可实现任务内从外层特权级变换到内层
特权级。
在上图所示的门描述符内偏移4字节的位0至位4是双字计数字段,该字段只在调用门描述符中有
效,在其它门描述符中无效。主程序通过堆栈把入口参数传递给子程序,如果在利用调用门调用
子程序时引起特权级的转换和堆栈的改变,那么就需要将外层堆栈中的参数复制到内层堆栈。该
双字计数字段就是用于说明这种情况发生时,要复制的双字参数的数量。
3.任务门
任务门指示任务。任务门内的选择子必须指示GDT中的任务状态段TSS描述符,门中的偏移无意
义。任务的入口点保存在TSS中。利用段间转移指令JMP和段间调用指令CALL,通过任务门可实
现任务切换。
4.中断门和陷阱门
中断门和陷阱门描述中断/异常处理程序的人口点。中断门和陷阱门内的选择子必须指向代码段
描述符,门内的偏移就是对应代码段的人口点的偏移。中断门和陷阱门只有在中断描述符表IDT中
才有效。关于中断门和陷阱门的区别将在以后的文章中论述。
<三>任务状态段
任务状态段(Task State Segment)是保存一个任务重要信息的特殊段。任务状态段描述符用于
描述这样的系统段。任务状态段寄存器TR的可见部分含有当前任务的任务状态段描述符的选择
子,TR的不可见的高速缓冲寄存器部分含有当前任务状态段的段基地址和段界限等信息。
TSS在任务切换过程中起着重要作用,通过它实现任务的挂起和恢复。所谓任务切换是指,挂起
当前正在执行的任务,恢复或启动另一任务的执行。在任务切换过程中,首先,处理器中各寄
存器的当前值被自动保存到TR所指定的TSS中;然后,下一任务的TSS的选择子被装入TR;最后,
从TR所指定的TSS中取出各寄存器的值送到处理器的各寄存器中。由此可见,通过在TSS中保存
任务现场各寄存器状态的完整映象,实现任务的切换。
任务状态段TSS的基本格式如下图所示。
任
  务
  状
  态
  段
  基
  本
  部
  分
  的
  格
  式 | 
BIT31—BIT16 | 
BIT15—BIT1 | 
BIT0 | 
Offset | 
| 0000000000000000 | 
链接字段 | 
0 | 
| ESP0 | 
4 | 
| 0000000000000000 | 
SS0 | 
8 | 
| ESP1 | 
0CH | 
| 0000000000000000 | 
SS1 | 
10H | 
| ESP2 | 
14H | 
| 0000000000000000 | 
SS2 | 
18H | 
| CR3 | 
1CH | 
| EIP | 
20H | 
| EFLAGS | 
24H | 
| EAX | 
28H | 
| ECX | 
2CH | 
| EDX | 
30H | 
| EBX | 
34H | 
| ESP | 
38H | 
| EBP | 
3CH | 
| ESI | 
40H | 
| EDI | 
44H | 
| 0000000000000000 | 
ES | 
48H | 
| 0000000000000000 | 
CS | 
4CH | 
| 0000000000000000 | 
SS | 
50H | 
| 0000000000000000 | 
DS | 
54H | 
| 0000000000000000 | 
FS | 
58H | 
| 0000000000000000 | 
GS | 
5CH | 
| 0000000000000000 | 
LDTR | 
60H | 
| I/O许可位图偏移 | 
000000000000000 | 
T | 
64H | 
从图中可见,TSS的基本格式由104字节组成。这104字节的基本格式是不可改变的,但在此之外
系统软件还可定义若干附加信息。基本的104字节可分为链接字段区域、内层堆栈指针区域、地
址映射寄存器区域、寄存器保存区域和其它字段等五个区域。
1.寄存器保存区域
寄存器保存区域位于TSS内偏移20H至5FH处,用于保存通用寄存器、段寄存器、指令指针和标志
寄存器。当TSS对应的任务正在执行时,保存区域是未定义的;在当前任务被切换出时,这些寄
存器的当前值就保存在该区域。当下次切换回原任务时,再从保存区域恢复出这些寄存器的值,
从而,使处理器恢复成该任务换出前的状态,最终使任务能够恢复执行。
从上图可见,各通用寄存器对应一个32位的双字,指令指针和标志寄存器各对应一个32位的双
字;各段寄存器也对应一个32位的双字,段寄存器中的选择子只有16位,安排再双字的低16位,
高16位未用,一般应填为0。
2.内层堆栈指针区域
为了有效地实现保护,同一个任务在不同的特权级下使用不同的堆栈。例如,当从外层特权
级3变换到内层特权级0时,任务使用的堆栈也同时从3级变换到0级堆栈;当从内层特权级0变
换到外层特权级3时,任务使用的堆栈也同时从0级堆栈变换到3级堆栈。所以,一个任务可能
具有四个堆栈,对应四个特权级。四个堆栈需要四个堆栈指针。
TSS的内层堆栈指针区域中有三个堆栈指针,它们都是48位的全指针(16位的选择子和32位的
偏移),分别指向0级、1级和2级堆栈的栈顶,依次存放在TSS中偏移为4、12及20开始的位置。
当发生向内层转移时,把适当的堆栈指针装入SS及ESP寄存器以变换到内层堆栈,外层堆栈的
指针保存在内层堆栈中。没有指向3级堆栈的指针,因为3级是最外层,所以任何一个向内层
的转移都不可能转移到3级。
但是,当特权级由内层向外层变换时,并不把内层堆栈的指针保存到TSS的内层堆栈指针区域。
实际上,处理器从不向该区域进行写入,除非程序设计者认为改变该区域的值。这表明向内层
转移时,总是把内层堆栈认为是一个空栈。因此,不允许发生同级内层转移的递归,一旦发生
向某级内层的转移,那么返回到外层的正常途径是相匹配的向外层返回。
3.地址映射寄存器区域
从虚拟地址空间到线性地址空间的映射由GDT和LDT确定,与特定任务相关的部分由LDT确定,
而LDT又由LDTR确定。如果采用分页机制,那么由线性地址空间到物理地址空间的映射由包含
页目录表起始物理地址的控制寄存器CR3确定。所以,与特定任务相关的虚拟地址空间到物理
地址空间的映射由LDTR和CR3确定。显然,随着任务的切换,地址映射关系也要切换。
TSS的地址映射寄存器区域由位于偏移1CH处的双字字段(CR3)和位于偏移60H处的字
字段(LDTR)组成。在任务切换时,处理器自动从要执行任务的TSS中取出这两个字段,分别
装入到寄存器CR3和LDTR。这样就改变了虚拟地址空间到物理地址空间的映射。
但是,在任务切换时,处理器并不把换出任务但是的寄存器CR3和LDTR的内容保存到TSS中的
地址映射寄存器区域。事实上,处理器也从来不向该区域自动写入。因此,如果程序改变
了LDTR或CR3,那么必须把新值人为地保存到TSS中的地址映射寄存器区域相应字段中。可以
通过别名技术实现此功能。
4.链接字段
链接字段安排在TSS内偏移0开始的双字中,其高16位未用。在起链接作用时,地16位保存前
一任务的TSS描述符的选择子。
如果当前的任务由段间调用指令CALL或中断/异常而激活,那么链接字段保存被挂起任务的
TSS的选择子,并且标志寄存器EFLAGS中的NT位被置1,使链接字段有效。在返回时,由
于NT标志位为1,返回指令RET或中断返回指令IRET将使得控制沿链接字段所指恢复到链上的
前一个任务。
5.其它字段
为了实现输入/输出保护,要使用I/O许可位图。任务使用的I/O许可位图也存放在TSS中,作
为TSS的扩展部分。在TSS内偏移66H处的字用于存放I/O许可位图在TSS内的偏移(从TSS开头
开始计算)。关于I/O许可位图的作用,以后的文章中将会详细介绍。
在TSS内偏移64H处的字是为任务提供的特别属性。在80386中,只定义了一种属性,即调试
陷阱。该属性是字的最低位,用T表示。该字的其它位置被保留,必须被置为0。在发生任务
切换时,如果进入任务的T位为1,那么在任务切换完成之后,新任务的第一条指令执行之前
产生调试陷阱。
6.用结构类型定义TSS
根据上图给出的任务状态段TSS的结构,可定义如下的TSS结构类型:
TSS             STRUC
TRLink          DW      0      
                DW      0      
TRESP0          DD      0      
TRSS0           DW      0      
                DW      0      
TRESP1          DD      0      
TRSS1           DW      0      
                DW      0      
TRESP2          DD      0      
TRSS2           DW      0      
                DW      0      
TRCR3           DD      0      
TREIP           DD      0      
TREFlag         DD      0      
TREAX           DD      0      
TRECX           DD      0      
TREDX           DD      0      
TREBX           DD      0      
TRESP           DD      0      
TREBP           DD      0      
TRESI           DD      0      
TREDI           DD      0      
TRES            DW      0      
                DW      0      
TRCS            DW      0      
                DW      0      
TRSS            DW      0      
                DW      0      
TRDS            DW      0      
                DW      0      
TRFS            DW      0      
                DW      0      
TRGS            DW      0      
                DW      0      
TRLDTR          DW      0      
                DW      0      
TRTrip          DW      0      
TRIOMap         DW      $+2    
TSS             ENDS
| 参考资料 | 
书        名 | 
出  版  社 | 
作    者 | 
| 《保护方式下的80386及其编程》 | 
清华大学出版社 | 
周明德主编 | 
| 《80X86汇编语言程序设计教程》 | 
清华大学出版社 | 
扬季文主编 |